#include "statsxx/machine_learning/activation_functions/softmax.hpp"

// STL
#include <cmath>             // std::exp()
#include <stdexcept>         // std::runtime_error()
#include <vector>            // std::vector<>

// jScience
#include "jScience/math.hpp" // Kronecker_delta()


inline activation_function::softmax::softmax() {};

inline activation_function::softmax::~softmax() {};


//
//
//
inline double activation_function::softmax::f(const double x)
{
    throw std::runtime_error("error in activation_function::softmax::f(): cannot calculate with a single input");
}

//
//
//
inline double activation_function::softmax::df(const double x)
{
    throw std::runtime_error("error in activation_function::softmax::df(): cannot calculate with a single input");
}

//
//
//
inline double activation_function::softmax::inv(const double fx)
{
    throw std::runtime_error("error in activation_function::softmax::inv(): cannot calculate");
}


//
//
//
inline std::vector<double> activation_function::softmax::f(std::vector<double> x)
{
    if( x.size() < 2 )
    {
        throw std::runtime_error("error in activation_function::softmax::f(): cannot calculate with a single input");
    }

    //=========================================================

    double sum = 0.;

    for( auto &xi : x )
    {
        xi   = std::exp(xi);
        sum += xi;
    }

    for( auto &xi : x )
    {
        xi /= sum;
    }

    return x;
}

//
// DESC: Derivative of the softmax activation function.
//
// INPUT:
//          x              : vector of output
//          k              : position of x to calculate softmax activation
//          j              : element to calculate derivative with respect to
//
// NOTE: The derivative is :
//
//          dy_k/da_j = y_k*(I_kj - y_j)
//
inline double activation_function::softmax::df(const std::vector<double> &x, int k, int j)
{
    std::vector<double> y = this->f(x);

    return ( y[k]*(Kronecker_delta(k,j) - y[j]) );
}
